#include "../inc/2048.h"
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <algorithm>


/*
4*4图片打印位置
第一个 5,5、第二个 124,5、第三个 243,5、第四个 362,5
第五个 5,125、第六个 124,125、第七个 243,125、第八个 362,125
第九个 5,243、第十个 124,243、第十一个 243,243、第十二个 362,243
第十三个 5,361、第十四个 124,361、第十五个 243,361、第十六个 362,361
*/


//创建2048的BMP对象
BMP _2048Empty("./2048/2048Empty.bmp");
BMP _0("./2048/0.bmp");
BMP _2("./2048/2.bmp");
BMP _4("./2048/4.bmp");
BMP _8("./2048/8.bmp");
BMP _16("./2048/16.bmp");
BMP _32("./2048/32.bmp");
BMP _64("./2048/64.bmp");
BMP _128("./2048/128.bmp");
BMP _256("./2048/256.bmp");
BMP _512("./2048/512.bmp");
BMP _1024("./2048/1024.bmp");
BMP _2048("./2048/2048.bmp");


/**
 * @brief Game2048 构造函数
 *
 * 初始化2048游戏，设置游戏板的大小，并初始化分数为0。
 *
 * @param size 游戏板的大小，即游戏板的行数和列数。
 */
Game2048::Game2048(int size) : boardSize(size), score(0) 
{
    // 初始化棋盘大小，并将棋盘所有元素初始化为0
    board.resize(boardSize, std::vector<int>(boardSize, 0));

    // 设置随机数种子为当前时间
    std::srand(std::time(0));
}

/**
 * @brief 初始化游戏
 *
 * 此函数初始化 2048 游戏的状态。
 *
 * 具体步骤如下：
 * 1. 将分数重置为 0。
 * 2. 初始化游戏板（board），将所有格子都设置为 0。
 * 3. 在游戏板上随机添加两个数字（2 或 4）。
 */
void Game2048::initialize() 
{
    score = 0;
    // 遍历棋盘每一行
    for (auto& row : board) 
    {
        // 将每一行的元素初始化为0
        std::fill(row.begin(), row.end(), 0);
    }
    // 初始添加两个数字
    addRandomTile();
    addRandomTile();
}

/**
 * @brief 打印游戏2048的棋盘
 *
 * 遍历棋盘数组，打印每个位置上的数值所对应的图片
 * 第一行 第一个5,5、  第二个 124,5、  第三个 243,5、  第四个 362,5
 * 第二行 第一个5,125、第二个 124,125、第三个 243,125、第四个 362,125
 * 第三行 第一个5,245、第二个 124,245、第三个 243,245、第四个 362,245
 * 第四行 第一个5,365、第二个 124,365、第三个 243,365、第四个 362,365
 * 打印出当前得分。
 */
void Game2048::printBoard() const 
{
    //打印游戏板
    _2048Empty.show(Lcd::instance()->addr(), 0, 0);

    // 遍历棋盘中的每一行
    for (int i = 0; i < 4; i++) 
    {
        // 遍历当前行中的每一个数字
        for (int j = 0; j < 4; j++) 
        {
            // 计算每个格子的坐标
            int x = 5 + j * 119;  // 5, 124, 243, 362
            int y = 5 + i * 120;  // 5, 125, 245, 365 (微调了y坐标间距)

            switch (board[i][j]) 
            {
                case 0:   _0.show(Lcd::instance()->addr(), x, y); break;
                case 2:   _2.show(Lcd::instance()->addr(), x, y); break;
                case 4:   _4.show(Lcd::instance()->addr(), x, y); break;
                case 8:   _8.show(Lcd::instance()->addr(), x, y); break;
                case 16:  _16.show(Lcd::instance()->addr(), x, y); break;
                case 32:  _32.show(Lcd::instance()->addr(), x, y); break;
                case 64:  _64.show(Lcd::instance()->addr(), x, y); break;
                case 128: _128.show(Lcd::instance()->addr(), x, y); break;
                case 256: _256.show(Lcd::instance()->addr(), x, y); break;
                case 512: _512.show(Lcd::instance()->addr(), x, y); break;
                case 1024:_1024.show(Lcd::instance()->addr(), x, y); break;
                case 2048:_2048.show(Lcd::instance()->addr(), x, y); break;
            }
        }
    }
    // 打印当前得分
    std::cout << "Score: " << score << "\n";
}

/**
 * @brief 移动2048游戏棋盘上的数字
 *
 * 根据给定的方向移动棋盘上的数字，并在移动后生成一个随机数字。
 *
 * @param direction 移动方向
 *                  - Direction::LEFT: 向左移动
 *                  - Direction::RIGHT: 向右移动
 *                  - Direction::UP: 向上移动
 *                  - Direction::DOWN: 向下移动
 *
 * @return 如果棋盘上有数字移动，则返回true；否则返回false
 */
bool Game2048::move(Game2048::Direction direction) 
{
    bool moved = false;
    switch (direction) 
    {
        case Direction::LEFT: moved = moveLeft(); break;
        case Direction::RIGHT: moved = moveRight(); break;
        case Direction::UP: moved = moveUp(); break;
        case Direction::DOWN: moved = moveDown(); break;
    }
    if (moved) 
    {
        addRandomTile();
    }
    return moved;
}

/**
 * @brief 获取当前得分,返回当前2048游戏的得分
 */
int Game2048::getScore() const 
{
    return score;
}

// 修改当前分数
void Game2048::setScore(int new_score)
{
    score = new_score;
}

/**
 * @brief 判断游戏是否获胜
 *
 * 检查游戏板（board）中是否存在值为2048的格子，若存在则表示游戏获胜，返回true；否则返回false。
 *
 * @return 若游戏获胜返回true，否则返回false
 */
bool Game2048::isWin() const 
{
    for (const auto& row : board) 
    {
        for (int num : row) 
        {
            if (num == 2048) 
            {
                return true;
            }
        }
    }
    return false;
}

/**
 * @brief 在2048游戏板上随机添加一个数字2或4的方块
 *
 * 在游戏板的空白位置随机选择一个位置，并随机放置一个数字2或4的方块。
 * 如果游戏板上没有空白位置，则不进行操作。
 */
void Game2048::addRandomTile() 
{
    std::vector<std::pair<int, int>> emptyCells;
    for (int i = 0; i < boardSize; ++i)
    {
        for (int j = 0; j < boardSize; ++j) 
        {
            if (board[i][j] == 0) 
            {
                emptyCells.emplace_back(i, j);
            }
        }
    }

    if (!emptyCells.empty()) 
    {
        int index = std::rand() % emptyCells.size();
        auto pos = emptyCells[index];
        board[pos.first][pos.second] = (std::rand() % 10 == 0) ? 4 : 2;
    }
}

/**
 * @brief 检查是否可以移动
 *
 * 检查游戏板是否有可移动的空格或可合并的相邻数字。
 *
 * @return 如果可以移动则返回true，否则返回false。
 */
bool Game2048::canMove() const 
{
    // 检查是否有空格
    for (int i = 0; i < boardSize; ++i) 
    {
        for (int j = 0; j < boardSize; ++j) 
        {
            if (board[i][j] == 0) 
            {
                return true;
            }
        }
    }

    // 检查水平方向是否有可合并的相邻数字
    for (int i = 0; i < boardSize; ++i) 
    {
        for (int j = 0; j < boardSize - 1; ++j) 
        {
            if (board[i][j] == board[i][j + 1]) 
            {
                return true;
            }
        }
    }

    // 检查垂直方向是否有可合并的相邻数字
    for (int j = 0; j < boardSize; ++j) 
    {
        for (int i = 0; i < boardSize - 1; ++i) 
        {
            if (board[i][j] == board[i + 1][j]) 
            {
                return true;
            }
        }
    }

    // 没有可移动的空格或合并，返回false
    return false;
}

/**
 * @brief 向左移动游戏板上的数字，合并相同数字
 */
bool Game2048::moveLeft() 
{
    bool moved = false;
    for (int i = 0; i < boardSize; ++i) 
    {
        // 移除0并合并相同数字
        std::vector<int> newRow;
        for (int j = 0; j < boardSize; ++j) 
        {
            if (board[i][j] != 0) 
            {
                newRow.push_back(board[i][j]);
            }
        }

        // 合并相同数字
        for (int j = 0; j < (int)newRow.size() - 1; ++j) 
        {
            if (newRow[j] == newRow[j + 1]) 
            {
                newRow[j] *= 2;
                score += newRow[j];
                newRow.erase(newRow.begin() + j + 1);
                moved = true;
            }
        }

        // 填充0
        while (newRow.size() < boardSize) 
        {
            newRow.push_back(0);
        }

        // 检查是否有变化
        if (!moved) 
        {
            for (int j = 0; j < boardSize; ++j) 
            {
                if (board[i][j] != newRow[j]) 
                {
                    moved = true;
                    break;
                }
            }
        }

        board[i] = newRow;
    }
    return moved;
}

/**
 * @brief 向右移动游戏板上的数字，合并相同数字
 */
bool Game2048::moveRight() 
{
    rotateBoard();
    rotateBoard();
    bool moved = moveLeft();
    rotateBoard();
    rotateBoard();
    return moved;
}

/**
 * @brief 向上移动游戏板上的数字，合并相同数字
 */
bool Game2048::moveUp() 
{
    rotateBoard();
    rotateBoard();
    rotateBoard();
    bool moved = moveLeft();
    rotateBoard();
    return moved;
}

/**
 * @brief 向下移动游戏板上的数字，合并相同数字
 */
bool Game2048::moveDown() 
{
    rotateBoard();
    bool moved = moveLeft();
    rotateBoard();
    rotateBoard();
    rotateBoard();
    return moved;
}

/**
 * @brief 旋转游戏棋盘
 *
 * 将2048游戏的棋盘顺时针旋转90度。
 *
 * @note 该函数会直接修改类成员变量 `board`。
 */
void Game2048::rotateBoard() 
{
    std::vector<std::vector<int>> newBoard(boardSize, std::vector<int>(boardSize));
    for (int i = 0; i < boardSize; ++i) 
    {
        for (int j = 0; j < boardSize; ++j) 
        {
            newBoard[j][boardSize - 1 - i] = board[i][j];
        }
    }
    board = newBoard;
}

// int main() 
// {
//     Game2048 game;
//     game.initialize();
//     game.printBoard();

//     char input;
//     while (!game.isGameOver() && !game.isWin()) 
//     {
//         std::cin >> input;
//         switch (input) 
//         {
//             case 'w': game.move(Game2048::Direction::UP); break;
//             case 's': game.move(Game2048::Direction::DOWN); break;
//             case 'a': game.move(Game2048::Direction::LEFT); break;
//             case 'd': game.move(Game2048::Direction::RIGHT); break;
//             default: continue;
//         }
//         game.printBoard();
//     }

//     if (game.isWin()) 
//     {
//         std::cout << "Congratulations! You won!\n";
//     } else 
//     {
//         std::cout << "Game Over!\n";
//     }

//     return 0;
// }
